/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.job;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import jp.aist.gtrc.plus.scheduler.node.NodeAllocateSet;
import jp.aist.gtrc.plus.scheduler.node.NodeResource;
import jp.aist.gtrc.plus.scheduler.specific.pbs.status.PBSJobID;
import jp.aist.gtrc.plus.scheduler.specific.pbs.status.PBSJobStatus;
import jp.aist.gtrc.plus.scheduler.status.JobStatus;
import jp.aist.gtrc.plus.scheduler.status.QueueStatus;
import jp.aist.gtrc.plus.scheduler.util.SortKey;


public enum JobSortKey implements SortKey<JobStatus> {

	JobPriority,
	LeastCPURequested,	MostCPURequested,
	LeastNodeRequested,	MostNodeRequested,
	LeastTimeRequested,	MostTimeRequested,
	SubmitTime,
	QueuePriority, QueueRoundRobin, ByQueue,
	ReservePrior,
        Deadline,
        PBSJobID,
	OwnersName("OwnersOrder"),
	OwnersGroup("GroupsOrder"),
	OwnersHost("HostsOrder");

	private final String confName;	// order title in scheduler config file

	JobSortKey(){
		this.confName = null;
	}

	JobSortKey(String confName){
		this.confName = confName;
	}

	public boolean needOrders() {

		return (confName != null);

	}

	public String getConfigTitle() {

		return confName;

	}

	// Point Map for Owners{Name/Group/Host}
	// String key: owner(or group etc)'s name
	// Integer value: owner(or group etc)'s point
	//	(0 is highest, Interger.MAX_VALUE is lowest)
	private Map<String, Integer> priority = null;

	public void setOrders(String[] names){

		assert(priority == null);

		if (needOrders() && (names != null)) {
			priority = new HashMap<String, Integer>();
			// names[0]'s job has highest priority
			// names[n]'s job has n-th priority
			// unspecified name's job has lowest priority
			int point = 0;
			for (String name : names) {
				priority.put(name, point);
				point++;
			}
		}

	}

	private int getPoint(String name){

		assert(needOrders() == true);
		assert(priority != null);
		assert(priority.size() > 0);

		Integer p = null;

		if (this == OwnersHost){
			// use suffix search:
			for (Map.Entry<String, Integer> e : priority.entrySet()){
				if (name.endsWith(e.getKey())) {
					p = e.getValue();
					break;
				}
			}
		} else {
			// use exact match:
			p = priority.get(name);
		}

		if (p != null) {
			// priority specified
			return p.intValue();
		} else {
			// others or setOrders() not called
			// MAX_VALUE means lowest priority
			return Integer.MAX_VALUE;
		}

	}

	private int getRequestedCPUNum(JobStatus job) {

		int sum = 0;
		for (NodeAllocateSet set : job.getNodeRequests()) {
			int n = 0;
			for (NodeResource rsc : set.getRequestedResources()) {
				n += rsc.getNodeCPUNum();
			}
			sum += n * set.getRequestedNum();
		}
		return sum;

	}

	private int getRequestedNodeNum(JobStatus job) {

		int sum = 0;
		for (NodeAllocateSet set : job.getNodeRequests()) {
			sum += set.getRequestedNum();
		}
		return sum;

	}

	public Comparator<JobStatus> getComparator(){

		switch(this){
		case JobPriority:
		    // highest(largest) priority job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					return arg1.getPriority() - arg0.getPriority();
				}
			};

		case LeastCPURequested:
		    // least(smallest) CPU requested job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					return getRequestedCPUNum(arg0) - getRequestedCPUNum(arg1);
				}
			};
		case MostCPURequested:
		    // most CPU requested job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					return getRequestedCPUNum(arg1) - getRequestedCPUNum(arg0);
				}
			};
		case LeastNodeRequested:
		    // least(smallest) Node requested job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					return getRequestedNodeNum(arg0) - getRequestedNodeNum(arg1);
				}
			};
		case MostNodeRequested:
		    // most Node requested job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					return getRequestedNodeNum(arg1) - getRequestedNodeNum(arg0);
				}
			};
		case LeastTimeRequested:
		    // least(shortest) Walltime requested job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					return arg0.getRequestedTime() - arg1.getRequestedTime();
				}
			};
		case MostTimeRequested:
		    // most(longest) Walltime requested job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					return arg1.getRequestedTime() - arg0.getRequestedTime();
				}
			};
		case SubmitTime:
			// first submit job is a first candidate
			return new Comparator<JobStatus>() {
			public int compare(JobStatus arg0, JobStatus arg1) {
				long d =
					arg0.getSubmitTime().getTimeInMillis()
					- arg1.getSubmitTime().getTimeInMillis();
				if (d > 0) {
					return 1;
				} else if (d < 0) {
					return -1;
				} else {
					return 0;
				}
			}
		};
		case QueuePriority:
			// a job in higher priority queue has higher priority
			return new Comparator<JobStatus>() {
			public int compare(JobStatus arg0, JobStatus arg1) {
				QueueStatus q0 = arg0.getQueueStatus();
				QueueStatus q1 = arg1.getQueueStatus();
				if ((q0 != null) && (q1 != null)) {
					return q1.getPriority() - q0.getPriority();
				} else {
					return 0;
				}
			}
		};
		case QueueRoundRobin:
		case ByQueue:
			// a job in smallest index queue has highest priority
			return new Comparator<JobStatus>() {
			public int compare(JobStatus arg0, JobStatus arg1) {
				QueueStatus q0 = arg0.getQueueStatus();
				QueueStatus q1 = arg1.getQueueStatus();
				if ((q0 != null) && (q1 != null)) {
					return q0.getQueueIndex() - q1.getQueueIndex();
				} else {
					return 0;
				}
			}
		};
		case ReservePrior:
			return new Comparator<JobStatus>() {
			public int compare(JobStatus arg0, JobStatus arg1) {
				boolean job0Rsvd = (arg0.getReserveId() != null);
				boolean job1Rsvd = (arg1.getReserveId() != null);
				/*
				 * j0		j1		result
				 * ----------------------------------------------
				 * Rsvd		Rsvd	same (0)
				 * Rsvd		no		j0 has higher priority (-1)
				 * no		Rsvd	j1 has higher priority (1)
				 * no		no		same (0)
				 */
				if (job0Rsvd) {
					return (job1Rsvd == true) ? 0 : -1;
				} else {
					return (job1Rsvd == true) ? 1 : 0;
				}
			}
		};

		case Deadline:
		    // earliest Deadline requested job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					if ((arg0 instanceof PBSJobStatus)
							&& (arg1 instanceof PBSJobStatus)) {
						PBSJobStatus p0 = (PBSJobStatus)arg0;
						PBSJobStatus p1 = (PBSJobStatus)arg1;
					        return p0.getDeadline() - p1.getDeadline();
                                         } else {
                                                return 0;
                                         }
				}
			};

		case PBSJobID:
		    //  smallest job ID is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					if ((arg0 instanceof PBSJobStatus)
							&& (arg1 instanceof PBSJobStatus)) {
						PBSJobID id0 = (PBSJobID) ((PBSJobStatus)arg0).getJobID();
						PBSJobID id1 = (PBSJobID) ((PBSJobStatus)arg1).getJobID();
					        return id0.compareTo(id1);
                                         } else {
                                                return 0;
                                         }
				}
			};

		case OwnersName:
		    // smallest point (= highest priority) owner's job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					return getPoint(arg0.getOwner())
						- getPoint(arg1.getOwner());
				}
			};
		case OwnersGroup:
		    // smallest point (= highest priority) owner's group job is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					if ((arg0 instanceof PBSJobStatus)
							&& (arg1 instanceof PBSJobStatus)) {
						PBSJobStatus p0 = (PBSJobStatus)arg0;
						PBSJobStatus p1 = (PBSJobStatus)arg1;
						return getPoint(p0.getOwnersGroup())
							- getPoint(p1.getOwnersGroup());
					} else {
						return 0;
					}
				}
			};
		case OwnersHost:
		    // smallest point (= highest priority) owner's host is a first candidate
			return new Comparator<JobStatus>() {
				public int compare(JobStatus arg0, JobStatus arg1) {
					if ((arg0 instanceof PBSJobStatus)
							&& (arg1 instanceof PBSJobStatus)) {
						PBSJobStatus p0 = (PBSJobStatus)arg0;
						PBSJobStatus p1 = (PBSJobStatus)arg1;
						return getPoint(p0.getOwnersHost())
							- getPoint(p1.getOwnersHost());
					} else {
						return 0;
					}
				}
			};

		default:
			assert(false);	// Not reached here
			return null;
		}

	}

}
